import { HttpStatus, Injectable, NestMiddleware } from '@nestjs/common';
import { AccessType } from 'src/shared/dto/access.dto';
import { Access } from 'src/shared/entities/access.entity';
import { AccessService } from 'src/shared/services/access.service';
import { match } from 'path-to-regexp';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(private readonly accessService: AccessService) {}
  async use(req: any, res: any, next: (error?: Error | any) => void) {
    const user = req.session.user;
    if (!user) {
      return res.redirect('/admin/logout');
    }
    res.locals.user = user; // 这个属性可以用来渲染模板
    const accessTree = await this.accessService.findAll(); // 获取全部资源
    const userAccessIds = this.getUserAccessIds(user); // 获取当前用户有权限的资源
    const menuTree = user.is_super
      ? accessTree
      : this.getUserMenuTree(accessTree, userAccessIds);
    res.locals.menuTree = menuTree;
    if (user.is_super || req.originalUrl === '/admin/welcome') {
      return next();
    }

    if (this.hasPermission(user, req.originalUrl)) {
      return next();
    } else {
      res.status(HttpStatus.FORBIDDEN).render('error', {
        message: '无权限访问此页面',
        layout: false,
      });
    }
  }
  private hasPermission(user, url) {
    const userAccessUrls: [] = user.roles.flatMap((role) =>
      role.accesses.map((access) => access.url),
    );
    return userAccessUrls
      .filter((item) => item)
      .some((urlPattern) => match(urlPattern)(url));
  }
  private getUserAccessIds(user): number[] {
    const userAccessIds = user.roles.flatMap((role) =>
      role.accesses.map((access) => access.id),
    );
    return [...new Set(userAccessIds).values()] as number[];
  }
  private getUserMenuTree(accessTree: Access[], userAccessIds: number[]) {
    return accessTree.filter((access) => {
      if (
        access.type === AccessType.FEATURE ||
        !userAccessIds.includes(access.id)
      ) {
        return false;
      }
      if (access.children) {
        access.children = this.getUserMenuTree(access.children, userAccessIds);
      }
      return true;
    });
  }
}
